import CollUtil from "@/utils/CollUtil";

export default {
  /**
   * 遍历树的节点（前序遍历）
   *
   * @param node          节点
   * @param subNodeGetter 获取子节点的方法
   * @param nodeHandler   节点处理器
   * @type T 节点类型
   */
  traverse<T>(
    node: T,
    subNodeGetter: (n: T) => T[] | null | undefined,
    nodeHandler: (n: T) => void
  ) {
    // handle current node
    nodeHandler(node);
    // traverse sub nodes
    const subNodes = subNodeGetter(node);
    if (subNodes == null) {
      return;
    }
    subNodes.forEach((tNode) =>
      this.traverse(tNode, subNodeGetter, nodeHandler)
    );
  },
  /**
   * 遍历树的节点（前序遍历）
   *
   * @param nodes         节点集合
   * @param subNodeGetter 获取子节点的方法
   * @param nodeHandler   节点处理器
   */
  traverse2<T>(
    nodes: T[] | null | undefined,
    subNodeGetter: (n: T) => T[] | null | undefined,
    nodeHandler: (n: T) => void
  ) {
    if (CollUtil.isEmpty(nodes)) {
      return;
    }
    nodes!.forEach((node) => this.traverse(node, subNodeGetter, nodeHandler));
  },
  /**
   * 遍历树的节点（前序遍历）
   * @param nodes 节点集合
   * @param subNodeGetter 获取子节点的方法
   * @param nodeHandler 节点处理器，第一个参数为父节点（可能为null），第二个参数为当前节点
   */
  traverse3<T>(
    nodes: T[] | null | undefined,
    subNodeGetter: (n: T) => T[] | null | undefined,
    nodeHandler: (pNode: T | null | undefined, node: T) => void
  ) {
    if (CollUtil.isEmpty(nodes)) {
      return;
    }
    nodes?.forEach((node) =>
      this.doTraverse3(null, node, subNodeGetter, nodeHandler)
    );
  },
  doTraverse3<T>(
    parentNode: T | null | undefined,
    node: T,
    subNodeGetter: (n: T) => T[] | null | undefined,
    nodeHandler: (pNode: T | null | undefined, node: T) => void
  ) {
    // handle current node
    nodeHandler(parentNode, node);
    // traverse sub nodes
    const subNodes = subNodeGetter(node);
    if (subNodes == null) {
      return;
    }
    subNodes.forEach((tNode) =>
      this.doTraverse3(node, tNode, subNodeGetter, nodeHandler)
    );
  },
  /**
   * 遍历层级
   *
   * @param nodes         当前层级的所有节点
   * @param subNodeGetter 获取子节点的方式
   * @param levelHandler  层级处理器，第一个参数为父节点（可能为null）,第二个参数为层级路径，第三个参数为当前层的所有节点
   */
  traverseLevel<T>(
    nodes: T[] | null | undefined,
    subNodeGetter: (n: T) => T[] | null | undefined,
    levelHandler: (pNode: T | null | undefined, path: T[], nodes: T[]) => void
  ) {
    if (CollUtil.isEmpty(nodes)) {
      return;
    }
    traverseLevel(null, [], nodes!, subNodeGetter, levelHandler);
  },
  /**
   * 遍历树的节点（前序遍历），并过滤
   *
   * @param nodes         节点集合
   * @param subNodeGetter 获取子节点的方法
   * @param nodeHandler   节点处理器，第一个参数为父节点（可能为null），第二个参数为当前节点，返回false将删除节点
   */
  filter<T>(
    nodes: T[] | null | undefined,
    subNodeGetter: (n: T) => T[] | null | undefined,
    nodeHandler: (pNode: T | null | undefined, node: T) => boolean
  ) {
    if (CollUtil.isEmpty(nodes)) {
      return;
    }
    for (let i = nodes!.length - 1; i >= 0; i--) {
      const node = nodes![i];
      const b = filter(null, node, subNodeGetter, nodeHandler);
      if (!b) {
        nodes!.splice(i, 1);
      }
    }
  },
  /**
   * 映射树
   * @param nodes 节点集合
   * @param subNodeGetter 获取子节点的方法
   * @param subNodeSetter 设置子节点的方法
   * @param mapper 映射器
   */
  map<T, U>(
    nodes: T[] | null | undefined,
    subNodeGetter: (n: T) => T[] | null | undefined,
    subNodeSetter: (u: U, subNodes: U[]) => void,
    mapper: (n: T) => U
  ) {
    if (CollUtil.isEmpty(nodes)) {
      return null;
    }
    return nodes!.map((node) =>
      map(node, subNodeGetter, subNodeSetter, mapper)
    );
  },
  /**
   * 映射树
   * @param node 节点
   * @param subNodeGetter 获取子节点的方法
   * @param subNodeSetter 设置子节点的方法
   * @param mapper 映射器
   */
  map2<T, U>(
    node: T | null | undefined,
    subNodeGetter: (n: T) => T[] | null | undefined,
    subNodeSetter: (u: U, subNodes: U[]) => void,
    mapper: (n: T) => U
  ) {
    if (node == null) {
      return null;
    }
    return map(node, subNodeGetter, subNodeSetter, mapper);
  },
};

/**
 * 遍历层级
 *
 * @param parentNode    层级的父节点
 * @param path          当前层级路径
 * @param subNodes      当前层级的所有节点
 * @param subNodeGetter 获取子节点的方式
 * @param levelHandler  层级处理器，第一个参数为父节点（可能为null），第二个参数为层级路径，第三个参数为当前层的所有节点
 */
function traverseLevel<T>(
  parentNode: T | null | undefined,
  path: T[],
  subNodes: T[],
  subNodeGetter: (n: T) => T[] | null | undefined,
  levelHandler: (pNode: T | null | undefined, path: T[], nodes: T[]) => void
) {
  // handle current level
  levelHandler(parentNode, path, subNodes);
  // traverse sub level
  for (const subNode of subNodes) {
    const childrenNodes = subNodeGetter(subNode);
    if (CollUtil.isNotEmpty(childrenNodes)) {
      const subPath = [...path, subNode];
      traverseLevel(
        subNode,
        subPath,
        childrenNodes!,
        subNodeGetter,
        levelHandler
      );
    }
  }
}

/**
 * 遍历树的节点（前序遍历），并过滤
 *
 * @param parentNode    父节点
 * @param node          节点
 * @param subNodeGetter 获取子节点的方法
 * @param nodeHandler   节点处理器，第一个参数为父节点（可能为null），第二个参数为当前节点，返回false将删除节点
 * @return 是否需要保留该节点，true保留，false删除
 */
function filter<T>(
  parentNode: T | null | undefined,
  node: T,
  subNodeGetter: (n: T) => T[] | null | undefined,
  nodeHandler: (pNode: T | null | undefined, node: T) => boolean
): boolean {
  // handle current node
  const test = nodeHandler(parentNode, node);
  if (!test) {
    return false;
  }
  // filter sub nodes
  const subNodes = subNodeGetter(node);
  if (CollUtil.isEmpty(subNodes)) {
    return test;
  }
  for (let i = subNodes!.length - 1; i >= 0; i--) {
    const subNode = subNodes![i];
    const b = filter(node, subNode, subNodeGetter, nodeHandler);
    if (!b) {
      subNodes!.splice(i, 1);
    }
  }
  return test;
}

/**
 * 映射树
 * @param node 节点
 * @param subNodeGetter 获取子节点的方法
 * @param subNodeSetter 设置子节点的方法
 * @param mapper 映射器
 */
function map<T, U>(
  node: T,
  subNodeGetter: (n: T) => T[] | null | undefined,
  subNodeSetter: (u: U, subNodes: U[]) => void,
  mapper: (n: T) => U
) {
  // map node
  const targetNode = mapper(node);
  // map children
  const subNodes = subNodeGetter(node);
  if (subNodes != null) {
    const targetSubNodes = subNodes.map((subNode) => mapper(subNode));
    subNodeSetter(targetNode, targetSubNodes);
  }
  return targetNode;
}
